package com.xyh.transaction.schedule;

import com.xyh.transaction.entity.Image;
import com.xyh.transaction.exception.BusinessException;
import com.xyh.transaction.mapper.CategoryMapper;
import com.xyh.transaction.mapper.GoodsMapper;
import com.xyh.transaction.mapper.ImageMapper;
import com.xyh.transaction.mapper.UserMapper;
import com.xyh.transaction.utils.CosImageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Configuration
@EnableScheduling
public class DeleteExpiredImageScheduler {

    @Autowired
    private ImageMapper imageMapper;
    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private UserMapper userMapper;

    @Value("${image.cosBucketName}")
    private String bucketName;
    @Value("${image.localDir}")
    private String localDir;
    @Value("${image.enableLocal}")
    private boolean enableLocal;

    @Scheduled(cron = "0 0 1 * * ?")
    @Transactional
    public void clearExpiredImages() {
        log.info("Starting scheduled task to clear expired images!");
        long start = System.currentTimeMillis();

        try {
            // Step 1: Collect expired images
            Set<Image> expiredImages = collectExpiredImages();

            // Step 2: Delete images from COS and local storage
            deleteImages(expiredImages);

            // Step 3: Delete unlinked images from local storage
            deleteUnlinkedLocalImages();

            // Step 4: Log task completion
            log.info("Scheduled task completed! Duration: {} ms", System.currentTimeMillis() - start);
        } catch (Exception e) {
            log.error("Scheduled task failed!", e);
        }
    }

    // Step 1: Collect expired images
    private Set<Image> collectExpiredImages() {
        List<Image> images = imageMapper.findAll();
        Map<Integer, Set<Image>> homeImagesMap = new HashMap<>();
        Map<Integer, Set<Image>> avatarImagesMap = new HashMap<>();
        Map<Integer, Set<Image>> categoryImagesMap = new HashMap<>();
        Map<Integer, Set<Image>> goodsImagesMap = new HashMap<>();

        images.forEach(image -> {
            Integer relatedId = image.getRelatedId();
            switch (image.getImageType()) {
                case BANNER:
                    homeImagesMap.computeIfAbsent(relatedId, k -> new HashSet<>()).add(image);
                    break;
                case CATEGORY:
                    categoryImagesMap.computeIfAbsent(relatedId, k -> new HashSet<>()).add(image);
                    break;
                case GOODS:
                    goodsImagesMap.computeIfAbsent(relatedId, k -> new HashSet<>()).add(image);
                    break;
                case AVATAR:
                    avatarImagesMap.computeIfAbsent(relatedId, k -> new HashSet<>()).add(image);
                    break;
                default:
                    throw new BusinessException("Invalid image type!");
            }
        });

        Set<Image> expiredImages = new HashSet<>();
        goodsImagesMap.forEach((key, value) -> {
            if (goodsMapper.findById(key) == null) {
                expiredImages.addAll(value);
            }
        });
        categoryImagesMap.forEach((key, value) -> {
            if (categoryMapper.findById(key) == null) {
                expiredImages.addAll(value);
            }
        });
        avatarImagesMap.forEach((key, value) -> {
            if (userMapper.findById(key) == null) {
                expiredImages.addAll(value);
            }
        });
        return expiredImages;
    }

    // Step 2: Delete images from COS and local storage, with transaction management
    @Transactional
    public void deleteImages(Set<Image> deleteImages) {
        List<Integer> deleteImageIds = deleteImages.parallelStream().map(Image::getId).collect(Collectors.toList());
        List<String> deleteImageUrls = deleteImages.parallelStream().map(Image::getUrl).collect(Collectors.toList());

        // 启用本地存储，无需从COS删除
        if (!enableLocal) {
            // Delete from COS storage
            deleteFromCos(deleteImageUrls);
        }

        // Delete from local storage
        deleteFromLocal(deleteImageUrls);

        // Delete records from database
        if (!deleteImageIds.isEmpty()) {
            imageMapper.deleteBatchByIds(deleteImageIds);
        }
    }

    // Step 3: 扫描本地目录并删除无关联的图片
    private void deleteUnlinkedLocalImages() {
        File directory = new File(localDir);
        if (!directory.exists() || !directory.isDirectory()) {
            log.warn("Local directory does not exist or is not a directory: {}", localDir);
            return;
        }

        // 获取本地目录下的所有文件名
        String[] localFiles = directory.list();
        if (localFiles == null) {
            log.warn("Failed to list files in local directory: {}", localDir);
            return;
        }

        // 获取数据库中所有图片的 URL
        List<String> dbImageUrls = imageMapper.findAll().stream()
                .map(Image::getUrl)
                .collect(Collectors.toList());

        // 遍历本地文件，检查是否与数据库中的 URL 匹配
        for (String fileName : localFiles) {
            if (!dbImageUrls.contains(fileName)) {
                // 如果本地文件不在数据库中，则删除
                File file = new File(localDir + fileName);
                if (file.isDirectory()) {
                    log.warn("Skipping directory: {}", fileName);
                    continue;
                }
                if (file.delete()) {
                    log.info("Successfully deleted unlinked local image: {}", fileName);
                } else {
                    log.error("Failed to delete unlinked local image: {}", fileName);
                }
            }
        }
    }


    // Delete images from COS storage
    private void deleteFromCos(List<String> deleteImageUrls) {
        try {
            CosImageUtil.deleteBatchImage(bucketName, deleteImageUrls);
        } catch (Exception e) {
            log.error("Failed to delete images from COS", e);
        }
    }

    // Delete images from local storage
    private void deleteFromLocal(List<String> deleteImageUrls) {
        for (String fileName : deleteImageUrls) {
            String localPath = localDir + fileName;
            File file = new File(localPath);
            if (file.exists()) {
                if (file.delete()) {
                    log.info("Successfully deleted local image: {}", localPath);
                } else {
                    log.error("Failed to delete local image: {}", localPath);
                }
            } else {
                log.warn("Local image not found: {}", localPath);
            }
        }
    }
}
